﻿

using Oddo.Concept.Crm.Common.Exceptions;

namespace Oddo.Concept.Crm.Common.Services
{
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Formatting;
    using System.Net.Http.Headers;
    using System.Threading;
    using Microsoft.Practices.ServiceLocation;
    using Newtonsoft.Json;
    using Oddo.Concept.Crm.Common.Attributes;
    using Oddo.Concept.Crm.Common.Communication.Interfaces;
    using Oddo.Concept.Crm.Common.Security;

    public abstract class ServiceProviderBase
    {
    }

    public abstract class ServiceClientBase
    {
        const string MEDIA_TYPE = "application/json";
        private IAuthenticationService _authenticationService;
        private readonly string _baseUrl;
        private readonly string _serverHost;
        private readonly MediaTypeFormatter _formatter;
        private readonly MediaTypeWithQualityHeaderValue _mediaType;
        private readonly JsonSerializer _jsonSerializer;

        protected ServiceClientBase(string serverHost, string baseUrl)
        {
            _serverHost = serverHost;
            _baseUrl = baseUrl;
            _formatter = new JsonMediaTypeFormatter();
            _mediaType = new MediaTypeWithQualityHeaderValue(MEDIA_TYPE);
            _jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings());
        }
        
        protected internal IAuthenticationService AuthenticationServiceProvider
        {
            get { return _authenticationService ??(_authenticationService = ServiceLocator.Current.GetInstance<IAuthenticationService>()); }
        }

        protected internal TResponse SendRequest<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken)
            where TRequest : IRequest
            where TResponse: IResponse, new()
        {
            var response = default(TResponse);

            using (var client = CreateHttpClient())
            using (var message = CreateRequestMessage(request))
                client.SendAsync(message)
                      .ContinueWith(task =>
                          {
                              try
                              {
                                  task.Result.EnsureSuccessStatusCode();
                                  task.Result.Content.ReadAsStreamAsync()
                                      .ContinueWith(st =>
                                          {
                                              using (var reader = new JsonTextReader(new StreamReader(st.Result)))
                                                  response = _jsonSerializer.Deserialize<TResponse>(reader);
                                          }, cancellationToken)
                                      .Wait();
                              }
                              catch (Exception exp)
                              {
                                  response = new TResponse { Exception = new RequestException("An error occured", exp) };
                              }
                          }, cancellationToken)
                      .Wait();

            return response;
        }

        protected internal HttpClient CreateHttpClient()
        {
            var client = new HttpClient(new HttpClientHandler
                {
                    Credentials = new NetworkCredential(AuthenticationServiceProvider.UserName, AuthenticationServiceProvider.UserPassword)
                }, true);
            client.DefaultRequestHeaders.Accept.Add(_mediaType);
            return client;
        }

        protected internal HttpRequestMessage CreateRequestMessage<TRequest>(TRequest request)
            where TRequest : IRequest
        {
            var attribute = (RequestActionNameAttribute)Attribute.GetCustomAttribute(request.GetType(), typeof(RequestActionNameAttribute), false);

            var uri = string.Format("{0}{1}{2}", _serverHost, _baseUrl, attribute.ActionName);
            if(!Uri.IsWellFormedUriString(uri, UriKind.RelativeOrAbsolute))
                throw new UriFormatException(string.Format("The uri \"{0}\" is not valid",uri));

            return new HttpRequestMessage(HttpMethod.Post, uri)
            {
                Content = new ObjectContent(typeof(TRequest), request, _formatter, MEDIA_TYPE)
            };
        }
    }
}